package com.andong.utils;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import static com.andong.utils.SystemConstant.*;

/**
 * 阿里云 OSS 工具类
 */
@Component
@Slf4j
public class AliOSSUtils {
    @Resource
    private AliOSSProperties aliOSSProperties;
    @Resource
    private RedisTemplate<String, Double> redisTemplate;
    /**
     * 实现上传图片到OSS
     */
    public String upload(MultipartFile file) throws IOException {
        long begin = System.currentTimeMillis();
        // 获取阿里云OSS参数
        String endpoint = aliOSSProperties.getEndpoint();
        String accessKeyId = aliOSSProperties.getAccessKeyId();
        String accessKeySecret = aliOSSProperties.getAccessKeySecret();
        String bucketName = aliOSSProperties.getBucketName();

        // 使用UUID生成唯一标识码避免文件覆盖
        String originalFilename = file.getOriginalFilename();
        assert originalFilename != null;
        String fileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));

        // 初始化OSS客户端
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 初始化分片上传
        InitiateMultipartUploadRequest initiateRequest = new InitiateMultipartUploadRequest(bucketName, fileName);
        InitiateMultipartUploadResult initiateResult = ossClient.initiateMultipartUpload(initiateRequest);
        String uploadId = initiateResult.getUploadId();
        // partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。
        List<PartETag> partETags = new ArrayList<>();
        try {
            // 上传分片
            final long partSize = 5 * 1024 * 1024L; // 设置分片大小为5MB
            long fileLength = file.getSize();
            int partCount = (int) (fileLength / partSize);
            if (fileLength % partSize != ZERO) {
                partCount++;
            }
            // 得到当前用户id
            Long id = (Long) BaseContext.getCurrentIdAndIdentity().get(CURRENT_ID);
            double progress = 0.0;
            redisTemplate.opsForValue().set("progress" + id, progress);
            for (int i = 0; i < partCount; i++) {
                long startPos = i * partSize;
                long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
                // 跳过已经上传的分片。
                InputStream inputStream = file.getInputStream();
                inputStream.skip(startPos);
                UploadPartRequest uploadPartRequest = new UploadPartRequest();
                uploadPartRequest.setBucketName(bucketName);
                uploadPartRequest.setKey(fileName);
                uploadPartRequest.setUploadId(uploadId);
                uploadPartRequest.setInputStream(inputStream);
                // 设置分片大小。除了最后一个分片没有大小限制，其他的分片最小为100 KB。
                uploadPartRequest.setPartSize(curPartSize);
                // 设置分片号。每一个上传的分片都有一个分片号，取值范围是1~10000，如果超出这个范围，OSS将返回InvalidArgument的错误码。
                uploadPartRequest.setPartNumber(i + 1);
                // 每个分片不需要按顺序上传，甚至可以在不同客户端上传，OSS会按照分片号排序组成完整的文件。
                UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
                // 每次上传分片之后，OSS的返回结果包含PartETag。PartETag将被保存在partETags中。
                partETags.add(uploadPartResult.getPartETag());
                progress = (i + 1) * 100.0 / partCount;
                // 将progress保留两位小数
                progress = Math.round(progress);
                // 将进度存入redis
                redisTemplate.opsForValue().set(VIDEO_UPLOAD_PROGRESS + id, progress);
            }

            CompleteMultipartUploadRequest uploadRequest = new CompleteMultipartUploadRequest(bucketName, fileName, uploadId, partETags);
            // 在完成文件上传的同时设置文件访问权限。
            // 完成上传。
            ossClient.completeMultipartUpload(uploadRequest);
            // 关闭OSSClient。
            ossClient.shutdown();
            // 构建文件URL
            String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;

            log.info("上传成功，耗时：{}s，文件URL：{}", (System.currentTimeMillis() - begin) / 1000, url);
            // 删除进度缓存
            redisTemplate.delete("progress" + id);
            return url;
        } finally {
            // 关闭ossClient
            ossClient.shutdown();
        }
    }

    public Double getProgress() {
        return redisTemplate.opsForValue().get("progress" + BaseContext.getCurrentIdAndIdentity().get(CURRENT_ID));
    }

    /**
     * 删除文件
     * @param url 原文件URL地址
     * @author XHU_WHY
     * @since 2024/1/13 10:51     */
    public void delete(String url) {
        //获取阿里云OSS参数
        String endpoint = aliOSSProperties.getEndpoint();
        String accessKeyId = aliOSSProperties.getAccessKeyId();
        String accessKeySecret = aliOSSProperties.getAccessKeySecret();
        String bucketName = aliOSSProperties.getBucketName();
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        // 获取文件名
        String objectName = url.substring(url.lastIndexOf("/") + 1);
        // 删除文件或目录。如果要删除目录，目录必须为空。
        ossClient.deleteObject(bucketName, objectName);
        ossClient.shutdown();
    }
}
